Amazon AppStream 2.0のスケーリング設定をおさらいする
しばたです。
突然ですがAppStream 2.0のスケーリング設定をおさらいしたいと思います。
「おさらい」と言いましたが、私自身がこれまでずっとこのスケーリング設定を根本的に勘違いしたままでおりつい先日そのことに気が付きました...
この記事で恥を晒すとともに皆さんが私と同じ轍を踏まない様にしてほしくてこの記事を書きました。
AppStream 2.0 フリートインスタンスの状態
スケーリングの話に入る前に、はじめに、AppStream 2.0フリートの各インスタンスが取りうる状態について解説します。
AppStream 2.0は公開アプリケーションのユーザー(セッション単位)が接続先のインスタンスを占有して利用し、セッションが終了するとそのインスタンスを破棄する挙動をします。
要はインスタンスを都度使い捨てています。
そしてフリートインスタンスは通常のEC2インスタンスとは異なりAppStream 2.0向けの初期化処理があるため、インスタンスが作成され始めてからユーザーが利用可能になるまで大体10分程度の時間を必要とします。
ここまでの内容を踏まえてフリートインスタンスは以下の状態を取り得ます。
- インスタンス準備中
- インスタンスの起動が完了し、ユーザーの接続待ち
- インスタンスの起動が完了し、ユーザー接続中
これらの状態に対してREST API(ComputeCapacityStatus)およびCloudWatch Metricsではそれぞれ以下の状態で表現されます。
状態 | REST API (ComputeCapacityStatus) |
CloudWatch Metrics (capacity値) |
備考 |
---|---|---|---|
インスタンス準備中 | - | Pending capacity | |
インスタンスの起動が完了し、ユーザーの接続待ち | Available | Available capacity | |
インスタンスの起動が完了し、ユーザー接続中 | InUse | InUse capacity | |
ユーザーの利用状態を問わず起動中 | Running | Actual capacity | Available capacity + InUse capacity |
両者だいたい同じ表現をしていますが、準備中インスタンスの数はCloudWatch Mectricsでのみ取得可能なのと、起動中の状態がRunning
とActual capacity
と若干異なる表現になっています。
Desired capacity
次にAppStream 2.0のスケーリングにおいて一番重要なパラメーターであるDesired capacity
について触れます。
Desired capacityは名前の通り「希望インスタンス数」であり、起動中インスタンス(Running
およびActual capacity
)の数をこの値に保とうとする値です。
たとえばDesired capacity = 5
にしてフリートを開始した場合、最初に5台のインスタンスを起動します。
10分程度待てばインスタンスの準備が完了しAvailable capacity = 5, InUse capacity = 0
となりユーザーの接続を待ち受けます。
ここで一人のユーザーがフリートを利用するとAvailable capacity = 4, InUse capacity = 1
になります。
まだ起動中インスタンスの総数は変わらないので変化はありません。
次にこのユーザーがセッションを終了した場合、InUseのインスタンスは破棄されAvailable capacity = 4, InUse capacity = 0
になります。
ここで起動中インスタンス総数が5から4に減ったためDesired capacity = 5
を維持しようと1台のインスタンスを新たに起動します。
これがDesired capcityの役割になります。
ちなみにREST APIおよびCloudWatch Metricsでは以下の状態で表現されます。
状態 | REST API (ComputeCapacityStatus) |
CloudWatch Metrics (capacity値) |
備考 |
---|---|---|---|
希望インスタンス数 | Desired | Desired capacity |
AppStream 2.0 フリートのスケーリング設定
ここからやっと本題に入ります。
AppStream 2.0 フリート[1]はオートスケーリングにより起動インスタンスの数を動的に変更することができます。
このスケーリング設定はApplication Auto Scalingにより実装されています。
AppStream 2.0のスケーリングを考える場合、AppStream 2.0とApplication Auto Scalingの2つのサービスを意識する必要があることをまず覚えておいてください。
そして、ここで一番重要なのが「AppStream 2.0ではDesired capacityしかスケーリングの指標を持っていない」という点です。
マネジメントコンソールからフリートの設定をしたことがある方ならMinimum capacity
やMaximum capacity
といった値を見たことがあるかと思いますが、これらの値はApplication Auto Scaling側のパラメーターであり、単にDesired capacityの値が上がり過ぎない(下がり過ぎない)様にするためのストッパーにすぎません。
実装が何であれAppStream 2.0のスケーリングは「フリートをスケールアップする場合はDesired capacityの値を増やす」、「フリートをスケールインする場合はDesired capacityの値を減らす」この2つの操作が全てです。
私はずっとこの仕組みを誤解してDesired capacityの値はオートスケーリングに関与せずにスケーリングの最後に行きつく値だとずっと思い込んでいました...
AppStream 2.0のオートスケーリングは「Application Auto Scalingを使ってDesired capacityの値を良い感じにスライドさせる」が正解です。
結果論ですがApplication Auto Scalingに慣れ親しんでいればこの様な勘違いはせずに済んだ気がします。
AppStream 2.0 でサポートされるスケーリングポリシー
Application Auto ScalingのスケーリングポリシーにおけるAppStream 2.0のサポート状況は以下となります。
ポリシー種別 | AppStream 2.0サポート | マネジメントコンソール | CLI | 備考 |
---|---|---|---|---|
ステップスケーリング | ○ | ○ | ○ | |
スケジュールされたスケーリング | ○ | ○ | ○ | 昔はマネジメントコンソールから設定できなかった記憶がある... |
ターゲット追跡スケーリング | ○ | - | ○ | 現状CLIからのみ設定可能 |
それぞれのスケーリングポリシーについて軽く触れていきます。
1. ステップスケーリング
まず最初に「ステップスケーリング」について説明します。
このポリシーが基本であり、インスタンス使用率などのメトリクス値を元にスケールアウト・スケールインを行います。
AppStream 2.0でサポートされているメトリクスは以下の通りです。
メトリクス | 単位 | 備考 |
---|---|---|
Capacity Utilization | 使用率(%) | インスタンス使用率。In use capacity / Actual capacity * 100 の値 |
Available capacity | インスタンス数 | |
Insufficient Capacity Error | エラー回数 | ユーザーが利用可能なインスタンスを見つけることができなかった際に発生するエラー |
スケールする単位は「インスタンス数」および「使用率(%)」となり内容に応じてDesired capacityの値が変動します。
具体的にスケールアウト・スケールインの設定をどうすべきかはシステム次第です。
とりあえずはマネジメントコンソールで表示されるデフォルト値から試して適宜調整していくのが良いでしょう。
- スケールアウト :
Capacity Utilization > 75%
になると2インスタンス 増やす (Desired capacityを2増やす)
- スケールイン :
Capacity Utilization < 25%
になると1インスタンス 減らす (Desired capacityを1減らす)
ステップスケーリングポリシーを設定するとCloudWatch Alarmが同時に作られ、スケールアウト・スケールインのトリガーとなります。
CLIからはaws application-autoscaling describe-scaling-policies
コマンドで詳細を確認できます。
# コマンド実行例
C:\> aws application-autoscaling describe-scaling-policies --service-namespace appstream --resource-id "fleet/my-test-fleet"
{
"ScalingPolicies": [
{
"PolicyARN": "arn:aws:autoscaling:ap-northeast-1:xxxxxxxxxxxx:scalingPolicy:1edc4f22-f987-4e60-9d19-abdff44f8cd1:resource/appstream/fleet/my-test-fleet:policyName/default-scale-in-1",
"PolicyName": "default-scale-in-1",
"ServiceNamespace": "appstream",
"ResourceId": "fleet/my-test-fleet",
"ScalableDimension": "appstream:fleet:DesiredCapacity",
"PolicyType": "StepScaling",
"StepScalingPolicyConfiguration": {
"AdjustmentType": "ChangeInCapacity",
"StepAdjustments": [
{
"MetricIntervalUpperBound": 0.0,
"ScalingAdjustment": -1
}
],
"Cooldown": 360,
"MetricAggregationType": "Average"
},
"Alarms": [
{
"AlarmName": "Appstream2-my-test-fleet-default-scale-in-1-Alarm",
"AlarmARN": "arn:aws:cloudwatch:ap-northeast-1:xxxxxxxxxxxx:alarm:Appstream2-my-test-fleet-default-scale-in-1-Alarm"
}
],
"CreationTime": "2022-02-07T12:58:34.198000+09:00"
},
{
"PolicyARN": "arn:aws:autoscaling:ap-northeast-1:xxxxxxxxxxxx:scalingPolicy:1edc4f22-f987-4e60-9d19-abdff44f8cd1:resource/appstream/fleet/my-test-fleet:policyName/default-scale-out-1",
"PolicyName": "default-scale-out-1",
"ServiceNamespace": "appstream",
"ResourceId": "fleet/my-test-fleet",
"ScalableDimension": "appstream:fleet:DesiredCapacity",
"PolicyType": "StepScaling",
"StepScalingPolicyConfiguration": {
"AdjustmentType": "ChangeInCapacity",
"StepAdjustments": [
{
"MetricIntervalLowerBound": 0.0,
"ScalingAdjustment": 2
}
],
"Cooldown": 120,
"MetricAggregationType": "Average"
},
"Alarms": [
{
"AlarmName": "Appstream2-my-test-fleet-default-scale-out-1-Alarm",
"AlarmARN": "arn:aws:cloudwatch:ap-northeast-1:xxxxxxxxxxxx:alarm:Appstream2-my-test-fleet-default-scale-out-1-Alarm"
}
],
"CreationTime": "2022-02-07T12:58:34.741000+09:00"
}
]
}
2. スケジュールされたスケーリング
次に「スケジュールされたスケーリング」について説明します。
こちらはCRON式などでスケーリングの実行をスケジュールすることができ、Minimum capacity
とMaximum capacity
の値を変更することができます。
Desired capacity
の値を直接変更することは出来ませんのでご注意ください。
このため大抵の場合において他のスケーリングポリシーと併用することになるでしょう。
例えば下図の様に毎朝(JST)にインスタンスの最小・最大を引き上げ、夜(JST)になったら引き下げを行うといった設定が可能となります。
ただし、スケーリング設定変更時にDesired capacity < Minimum capacity
になる場合はDesired capacity = Minimum capacity
までDesired capacityの値が引き上げられます。
逆にDesired capacity > Maximum capacity
になる場合はDesired capacity = Maximum capacity
までDesired capacityの値が引き下げられます。
この特性を活かすことで間接的にある程度Desired capacityの値を操作することは可能です。
CLIからはaws application-autoscaling describe-scheduled-actions
コマンドで詳細を確認できます。
# コマンド実行例
C:\> aws application-autoscaling describe-scheduled-actions --service-namespace appstream --resource-id "fleet/my-test-fleet"
{
"ScheduledActions": [
{
"ScheduledActionName": "daily-morning",
"ScheduledActionARN": "arn:aws:autoscaling:ap-northeast-1:xxxxxxxxxxxx:scheduledAction:1edc4f22-f987-4e60-9d19-abdff44f8cd1:resource/appstream/fleet/my-test-fleet:scheduledActionName/daily-morning",
"ServiceNamespace": "appstream",
"Schedule": "cron(0 22 * * ? *)",
"ResourceId": "fleet/my-test-fleet",
"ScalableDimension": "appstream:fleet:DesiredCapacity",
"StartTime": "2022-02-01T09:00:00+09:00",
"ScalableTargetAction": {
"MinCapacity": 5,
"MaxCapacity": 10
},
"CreationTime": "2022-02-07T17:48:33.910000+09:00"
},
{
"ScheduledActionName": "daily-evening",
"ScheduledActionARN": "arn:aws:autoscaling:ap-northeast-1:xxxxxxxxxxxx:scheduledAction:1edc4f22-f987-4e60-9d19-abdff44f8cd1:resource/appstream/fleet/my-test-fleet:scheduledActionName/daily-evening",
"ServiceNamespace": "appstream",
"Schedule": "cron(0 13 * * ? *)",
"ResourceId": "fleet/my-test-fleet",
"ScalableDimension": "appstream:fleet:DesiredCapacity",
"StartTime": "2022-02-01T09:00:00+09:00",
"ScalableTargetAction": {
"MinCapacity": 1,
"MaxCapacity": 3
},
"CreationTime": "2022-02-07T17:48:34.194000+09:00"
}
]
}
3. ターゲット追跡スケーリング
最後に「ターゲット追跡スケーリング」について説明します。
ターゲット追跡スケーリングは所定のメトリクス値を維持しようとするオートスケールを行うポリシーとなります。
AppStream 2.0ではAppStreamAverageCapacityUtilization
という名前でインスタンス使用率(%)の値を維持しようします。
(現状サポートされているメトリクスはこれだけであり他にはありません)
ターゲット追跡スケーリングはCLIからのみ設定可能で、例えばインスタンス使用率を80%維持にしたい場合は以下の様なコマンドとなります。
# AWS CLI on PowerShell での実行例
$resourceId = "fleet/my-test-fleet"
$json = @"
{
"PolicyName":"target-tracking-scaling-policy",
"ServiceNamespace":"appstream",
"ResourceId":"$resourceId",
"ScalableDimension":"appstream:fleet:DesiredCapacity",
"PolicyType":"TargetTrackingScaling",
"TargetTrackingScalingPolicyConfiguration":{
"TargetValue":80.0,
"PredefinedMetricSpecification":{
"PredefinedMetricType":"AppStreamAverageCapacityUtilization"
},
"ScaleOutCooldown":300,
"ScaleInCooldown":300
}
}
"@ -replace '"','\"'
aws application-autoscaling put-scaling-policy --cli-input-json $json
- Amazon AppStream 2.0 向け Fleet Auto Scaling の例をちょっとだけ改変
この実行結果は以下の様になり、スケールアウト・スケールインのトリガーとなる2つのCloudWatch Alarmが同時に生成されます。
# コマンド実行結果
C:\> aws application-autoscaling put-scaling-policy --cli-input-json $json
{
"PolicyARN": "arn:aws:autoscaling:ap-northeast-1:xxxxxxxxxxxx:scalingPolicy:1edc4f22-f987-4e60-9d19-abdff44f8cd1:resource/appstream/fleet/my-test-fleet:policyName/target-tracking-scaling-policy",
"Alarms": [
{
"AlarmName": "TargetTracking-fleet/my-test-fleet-AlarmHigh-404bd8fc-16ae-4fe0-abf1-fa044883bc26",
"AlarmARN": "arn:aws:cloudwatch:ap-northeast-1:xxxxxxxxxxxx:alarm:TargetTracking-fleet/my-test-fleet-AlarmHigh-404bd8fc-16ae-4fe0-abf1-fa044883bc26"
},
{
"AlarmName": "TargetTracking-fleet/my-test-fleet-AlarmLow-5cfe33eb-ddb1-4b96-b98d-acadc164066c",
"AlarmARN": "arn:aws:cloudwatch:ap-northeast-1:xxxxxxxxxxxx:alarm:TargetTracking-fleet/my-test-fleet-AlarmLow-5cfe33eb-ddb1-4b96-b98d-acadc164066c"
}
]
}
AlarmHigh
の方のアラームはインスタンス使用率(3分)が80%を超えると発動し、使用率を下げるためにスケールアウトします。
逆にAlarmLow
の方のアラームはインスタンス使用率(15分)が72%未満になると発動し、使用率を上げるためにスケールインします。
ドキュメントにある様に若干緩やかにスケールインする設定となっています。
ちなみにマネジメントコンソール上では何故かスケールアウトポリシーの形で表示されるのですが、内容も微妙におかしいのでGUI上では触れない方が良いと思います。
ちなみにポリシーを削除したい場合はaws application-autoscaling delete-scaling-policy
コマンドを使います。
# ポリシー削除例 on PowerShell
aws application-autoscaling delete-scaling-policy --service-namespace appstream `
--resource-id "fleet/my-test-fleet" `
--policy-name "target-tracking-scaling-policy" `
--scalable-dimension "appstream:fleet:DesiredCapacity"
補足 : ターゲット追跡スケーリング例
ターゲット追跡スケーリングはインスタンス使用率の変動を追跡するため、規模の小さい(インスタンス数の少ない)環境では使用率の変動が激しすぎてあまり期待した挙動にならないと思います。
このポリシーはある程度の規模がある環境向きです。
一応、手元の環境でMinimum capacity = 1, Maximum capacity = 10
、初期 Desired capacity = 1
で前述の例(インスタンス使用率80%維持
)を試した結果を記載しておきます。
No. | 状態 | Desired capacity | Available | InUse | 使用率(%) | 備考 |
---|---|---|---|---|---|---|
1 | 利用開始 | 1 | 1 | 0 | 0% | |
2 | スケールイン(不発) | 1 | 1 | 0 | 0% | CloudWacth Alarmが発火するが、これ以上下げようがないので何もしない |
2 | ユーザーがインスタンス利用 | 1 | 1 | 1 | 100% | 使用率100%に更新 |
3 | スケールアウト発動 | 2 | 1 | 1 | 50% | 使用率50%までダウン |
4 | ユーザーがインスタンス利用 | 2 | 0 | 2 | 100% | 使用率100%に更新 |
5 | スケールアウト発動 | 3 | 1 | 2 | 66% | 使用率66%までダウン |
6 | スケールイン(不発) | 3 | 1 | 2 | 66% | CloudWacth Alarmは発火するがスケールインせず |
7 | ユーザーがログアウト | 3 | 1 | 1 | 50% | |
8 | インスタンス補充 | 3 | 2 | 1 | 33% | Desired capacityが変動してないので補充される |
9 | スケールイン | 2 | 1 | 1 | 50% | ここでスケールイン発動 |
10 | ユーザーがログアウト | 2 | 1 | 0 | 0% | 全ユーザーログアウト |
11 | インスタンス補充 | 2 | 2 | 0 | 0% | Desired capacityが変動してないので補充される |
12 | スケールイン | 1 | 1 | 0 | 0% | 最後のスケールイン発動 |
規模が小さすぎて使用率の変動がめちゃくちゃなのが見て取れますね。
それでもそれなりに動作しますので、細かいことを考えたくない場合はこちらのポリシーを使っても良いかもしれません。
最後に
以上となります。
これまで間違って覚えていたAppStream 2.0のオートスケーリングをおさらいしてみました。
AppStream 2.0ではなくApplication Auto Scalingを中心にして考えると勘違いすることも無かったのかなという感じです。
本記事が皆さんの役に立てば嬉しいです。
厳密にはElastic Fleets以外のフリート ↩︎